package edu.lmu.cs.forney.roflkode.entities;

import java.util.ArrayList;
import java.util.List;

import edu.lmu.cs.forney.util.Log;

/**
 * A block, which is a container of a sequence of statements with
 * its own symbol table.
 */
public class Block extends Entity {
    private List<Statement> statements;
    private SymbolTable table = null;

    /**
     * Create a block with the given statement list.
     */
    public Block(List<Statement> statements) {
        this.statements = statements;
    }

    /**
     * Returns the list of all the statements immediately within
     * this block, in the order they are declared.
     */
    public List<Statement> getStatements() {
        return statements;
    }

    /**
     * Returns the list of all the types declared immediately within
     * this block, in the order they are declared.
     */
    public List<Type> getTypes() {
        List<Type> result = new ArrayList<Type>();
        for (Statement s: statements) {
            if (s instanceof Declaration) {
                Declarable d = ((Declaration)s).getDeclarable();
                if (d instanceof Type) {
                    result.add((Type)d);
                }
            }
        }
        return result;
    }

    /**
     * Returns the list of all the functions declared immediately within
     * this block, in the order they are declared.
     */
    public List<Function> getFunctions() {
        List<Function> result = new ArrayList<Function>();
        for (Statement s: statements) {
            if (s instanceof Declaration) {
                Declarable d = ((Declaration)s).getDeclarable();
                if (d instanceof Function) {
                    result.add((Function)d);
                }
            }
        }
        return result;
    }

    /**
     * Returns the block's symbol table.
     */
    public SymbolTable getTable() {
        return table;
    }

    /**
     * Creates the symbol table for this block.
     */
    public void createTable(SymbolTable parent) {
        if (parent != null) {
            // TODO: Throw exception if table already present?
        }
        table = new SymbolTable(parent);
    }

    /**
     * Performs semantic analysis on this block.
     */
    public void analyze(Log log, SymbolTable outer, Function owner,
            boolean inLoop) {
//        List<Type> types = getTypes();
//        List<Function> functions = getFunctions();
//
//        // Create the table if it hasn't already been created.  For blocks
//        // that are bodies of functions or for-statements, the analyze()
//        // method of the function or statmement will have created this
//        // table already, since it is the table in which the parameters
//        // or for-statement index belong.  For blocks that are programs,
//        // the table will have already been created, too.  All other
//        // blocks will need their tables created here.
//        if (table == null) {
//            table = new SymbolTable(outer);
//        }
//
//        // Struct types should go into the table first.  They can be
//        // used for everything in this scope: function return types,
//        // parameter types, variable types, field types, ....  Note
//        // that they are going into the symbol table WITHOUT being
//        // analyzed, because when analyzing them we have to check the
//        // types of their fields, and these fields may refer to other
//        // struct types declared in this block.
//        for (Type type: types) {
//            table.insert(type, log);
//        }
//
//        // Pre-analyze structure types so the fields are available.
//        // This has to be done AFTER all the struct types have been
//        // added to the symbol table, but BEFORE any variables are
//        // handled, since the variables may refer to struct fields in
//        // their initializing expressions.
//        for (Type type: types) {
//            type.analyze(log, table);
//        }
//
//        // Insert the functions into the table, but analyze ONLY the
//        // parameters and return types.  We can't analyze the function
//        // bodies until all the functions have been put into the
//        // table (with analyzed parameters) because within any function
//        // body there can be a call to any other function, and we have
//        // to be able to analyze the call.  Notice also that the
//        // functions are going in before any variables are being looked
//        // at since variables can call any function in their initializing
//        // expressions.
//        for (Function function: functions) {
//            function.analyzeSignature(log, table);
//            table.insert(function, log);
//        }
//
//        // Now just go through all the items in order and analyze
//        // everything, inserting variables as you go.  The variables
//        // have to be inserted during this final pass, since they are
//        // only in scope from their point of declaration onward.
//        // (In other words, if we tried to first insert all the variables
//        // and then analyze them later, that would have been wrong.)
//        for (Statement s: statements) {
//            if (s instanceof Declaration) {
//                Declarable d = ((Declaration)s).getDeclarable();
//                if (d instanceof Variable) {
//                    table.insert(d, log);
//                }
//                if (d instanceof Type) {
//                    // Don't analyze types again
//                    continue;
//                }
//            }
//            s.analyze(log, table, owner, inLoop);
//        }
    }
}
